<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>原型对象</title>
        <script>
            //原型对象
            //概念：每创建一个函数，解析器都会向函数添加一个属性prototype，
            //     这个属性对应的对象就称为原型对象；

            //普通函数的该属性无任何作用，而构造函数的prototype有以下特点：
            //1.构造函数被调用时，该函数创建的所有对象(实例)都有一个隐含属性，都指向构造函数的原型对象；
            //2.这些对象(实例)的隐含属性可以用__proto__来访问；

            //原理：
            //1.原型对象相当于公共的区域，所有同一类的实例都可以访问，
            //  所以，可以将所有对象(实例)共有的属性统一添加在构造函数的原型对象中；
            //2.原型链
            //  当访问一个对象的属性或方法时，会先在自身寻找，如果有则直接寻找，
            //  如果没有则在其构造函数的原型对象中寻找，这样依次向上一层寻找，
            //  直至找到object的原型，如果object的原型中依然没有该属性，则返回undefined；
            //3.构造函数名.prototype 和 对象名.__proto__ 都是指对应的原型对象；

            //创建一个构造函数
            function MyClass(){

            }
            var mc = new MyClass();
            //向原型中添加属性
            MyClass.prototype.a = "原型对象中的a";
            mc.__proto__.b = "原型对象中的b";

            console.log(MyClass.prototype === mc.__proto__); //true
            console.log(mc.a);                    //原型对象中的a
            console.log(mc.b);                    //原型对象中的b
            console.log(mc.__proto__.a);          //原型对象中的a
            console.log(MyClass.prototype.b);     //原型对象中的b

            //使用in检查属性时，如果对象中没有而原型中有该属性，仍返回true；
            console.log("a" in mc);               //true
            //使用对象的hasOwnProperty()方法，只有对象自身中含有某属性时才返回true；
            console.log(mc.hasOwnProperty("a") );  //false

            //mc自身中不含有hasOwnProperty属性，说明该属性可能在上一级原型中
            console.log(mc.hasOwnProperty("hasOwnProperty"));                   //false（说明mc自身不含该方法）
            console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));         //false (说明mc的原型中也不含该方法)
            console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //true(说明mc的原型的原型中含有该方法)



            //toString()方法重写
            //当在页面直接打印一个对象时，实际上输出的是对象的toString()方法的返回值
            function Person(name, age, gender){
                this.name = name;
                this.age = age;
                this.gender = gender;
            }
            //所以，可以通过改写原型对象的toString()方法来改变原有的object返回值
            //修改person原型的tostring
            Person.prototype.toString = function(){
                return "Person[name="+this.name+", age="+this.age+", gender="+this.gender+"]";
            }
            var per = new Person("孙悟空", 25, "男");
            var per2 = new Person("猪八戒", 18, "男");
            console.log(per);             //Person {name: '孙悟空', age: 25, gender: '男'}
            console.log(per2);            //Person {name: '猪八戒', age: 18, gender: '男'}  
            console.log(per.toString());  //Person[name=孙悟空, age=25, gender=男] 
            console.log(per2.toString()); //Person[name=猪八戒, age=18, gender=男]
            //由于per原型中的toString方法未赋值，所以下面的结果都是undefined
            console.log(per.__proto__.toString()); //Person[name=undefined, age=undefined, gender=undefined]  

            //toString()方法在对象的原型的原型中
            console.log(per.__proto__.__proto__.hasOwnProperty("toString"));  //true
        </script>
    </head>
    <body>
        
    </body>
</html>